package com.xf.system.utils.listCompare;

import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * @author xiefengf
 * <p>
 * 复杂集合比较器
 * 注：如果以主键id比较，且id字段为继承而来,否则请更改下面的代码。
 * Field idField = item.getClass().getSuperclass().getDeclaredField("id");
 */
@Component
public class ListCompareUtil<T> {
    /**
     * 比较两个List集合的工具类
     * @param currentList 当前集合 --- 入参数据
     * @param targetList 目标集合 --- 数据库数据
     * @param primaryKey 唯一标识（可变形参） --- "id" 或 "accountId",“deployment_name”
     * @return
     */
    public ListCompareResult<T> compareLists(List<T> currentList, List<T> targetList,String... primaryKey) {
        // 存放已添加的元素
        List<T> saveList = new ArrayList<>();
        // 存放已移除的元素
        List<T> deleteList = new ArrayList<>();
        // 存放需要更新的元素
        List<T> updateList = new ArrayList<>();

        // 将当前集合中的元素添加到 Map 中，以便快速查找
        Map<Object, T> currentMap = new HashMap<>();
        for (T currentItem : currentList) {
            // 获取唯一标识字段
            StringBuilder key = new StringBuilder();
            for (String str : primaryKey) {
                String id = String.valueOf(getId(currentItem,str));
                key.append(id);
            }
            currentMap.put(key.toString(), currentItem);
        }

        // 将目标集合中的元素添加到 Map 中，以便快速查找
        Map<Object, T> targetMap = new HashMap<>();
        for (T targetItem : targetList) {
            // 获取唯一标识字段
            StringBuilder key = new StringBuilder();
            for (String str : primaryKey) {
                String id = String.valueOf(getId(targetItem,str));
                key.append(id);
            }
            targetMap.put(key.toString(), targetItem);
        }

        // 遍历当前集合进行比较
        for (T currentItem : currentList) {
            // 如果目标集合中包含此元素(以primaryKey唯一标识值判断)
            StringBuilder key = new StringBuilder();
            for (String str : primaryKey) {
                String id = String.valueOf(getId(currentItem,str));
                key.append(id);
            }
            String currentId = key.toString();
            if (targetMap.containsKey(currentId)) {
                T targetItem = targetMap.get(currentId);
                // 如果两个对象中的有某一个字段不相等，则更新。否则，不做任何操作
                if (!compareObjects(currentItem,targetItem)){
                    // 当前元素需要更新
                    updateList.add(currentItem);
                }
            } else {
                // 当前元素需要新增
                saveList.add(currentItem);
            }
        }

        // 遍历目标集合，找出需要删除的元素
        for (T targetItem : targetList) {
            // 如果目标集合中包含此元素(以primaryKey唯一标识值判断)
            // 获取唯一标识字段
            StringBuilder key = new StringBuilder();
            for (String str : primaryKey) {
                String id = String.valueOf(getId(targetItem,str));
                key.append(id);
            }
            String targetId = key.toString();
            // 如果当前集合中已经没有此元素id，代表是需要删除的元素
            if (!currentMap.containsKey(targetId)) {
                // 目标元素需要删除
                deleteList.add(targetItem);
            }
        }

        return new ListCompareResult<>(saveList, deleteList, updateList);
    }

    /**
     * 获取对象的唯一标识ID，通过反射获取
     * @param item
     * @return
     */
    private Object getId(T item,String primaryKey) {
        if (item != null) {
            try {
                Field idField;
                // 由于一般实体类都是继承一个公共的实体类。如果使用id主键作为唯一标识。反射需要获取父class才能拿到id
                if ("id".equals(primaryKey)){
                    idField = item.getClass().getSuperclass().getDeclaredField(primaryKey);
                }else {
                    idField = item.getClass().getDeclaredField(primaryKey);
                }
                idField.setAccessible(true);
                return idField.get(item);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("反射异常");
            }
        }
        return null;
    }

    /**
     * 通用对象比较器方法
     * @param obj1
     * @param obj2
     * @return
     */
    private static boolean compareObjects(Object obj1, Object obj2) {
            // 如果两个对象是同一个引用，直接返回 true
            if (obj1 == obj2) {
                return true;
            }
            // 如果有一个对象为 null，或者两个对象的类型不匹配，则返回 false
            if (obj1 == null || obj2 == null || obj1.getClass() != obj2.getClass()) {
                return false;
            }
            // 获取对象的类
            Class<?> clazz = obj1.getClass();
            // 获取对象的所有字段
            Field[] fields = clazz.getDeclaredFields();
            // 循环每个字段都判断
            for (Field field : fields) {
                // 检查字段的修饰符是否为 private
                if (Modifier.isPrivate(field.getModifiers())) {
                    // 设置字段为可访问
                    field.setAccessible(true);
                }
                try {
                    Object fieldValue1 = field.get(obj1);
                    Object fieldValue2 = field.get(obj2);
                    if (!Objects.equals(fieldValue1, fieldValue2)) {
                        return false;
                    }
                } catch (IllegalAccessException e) {
                    // 处理反射访问异常
                    throw new RuntimeException("反射异常");
                }
            }
            return true;
        }


    /**
     * 用于存放比较结果的内部类
     * @param <T>
     */
    public static class ListCompareResult<T> {
        private List<T> saveList;
        private List<T> deleteList;
        private List<T> updateList;

        // 构造函数
        public ListCompareResult(List<T> saveList, List<T> deleteList, List<T> updateList) {
            this.saveList = saveList;
            this.deleteList = deleteList;
            this.updateList = updateList;
        }

        // 获取已添加的元素列表
        public List<T> getSaveList() {
            return saveList;
        }

        // 获取已移除的元素列表
        public List<T> getDeleteList() {
            return deleteList;
        }

        // 获取需要更新的元素列表
        public List<T> getUpdateList() {
            return updateList;
        }

    }
}

